Beetle双活实践
作者|陈秋
Beetle是转转公司测试部主导研发的一款基于gitlab,jenkins,maven为主的轻量级效率平台,主要解决编译部署测试发布流程中的效率问题,详细介绍请参考《效能提升的江湖路--转转Beetle平台百天记》。
随着全公司集群推广接入,beetle使用频率越来越高,作为单集群部署的beetle,每一次迭代,bug修复都需要停止服务。作为效能提高平台,本身停服就是在影响效率,这对使用方来讲是不可接受的。多活是在这个场景下提出的。这篇分享,主要和大家交流下beetle在多活场景下碰到的问题,和解决方案。
升级或bug修复等服务重启过程中,做搭配用户无感知。为了达到这个目的,我们需要解决以下几个问题。
Beetle服务,通过向编译服务,环境部署服务,上线服务发送命令,并轮询结果。来完成交互
session共享问题,多台的情况下,同一个客户端请求可能落在不同的服务器上
同步锁问题,单台情况下,原子操作大多使用了synchronized关键字,在多台情况下,这个是无法保证原子的
服务停止后,进行中的编译和部署在多台的情况下如何恢复?
一、session问题
session共享,一般有如下几种解决方案
服务间session同步,登录到一台服务器之后,将session同步到其他服务器
memcached-session-manager,俗称MSM
使用filter方式进行拦截过滤
持久化到数据库
我们使用上面的过滤器方式,在登录拦截器中,保存session。这样session不存在于服务器上时,就自动加上了。
二、同步锁问题,使用分布式锁来解决,一般三种,数据库,缓存,zookeeper
1、 使用数据库,插入数据一条记录作为锁,使用完之后,删除就好(key唯一性约束)。
问题:
加完锁程序挂了怎么办?
没有超时时间,一直占用怎么办?
插入数据失败怎么办?
解决:
使用定时任务轮询,删除过期的锁
while轮询insert?
实现起来就复杂了
2、使用缓存,比如利用redis的原子性,完成加锁,解锁,设置过期时间(setnx,del)实现分布式锁
问题:过期时间设置多少合适?
少了,没有执行完,锁就释放了
多设置一些,在处理完了之后,代码释放这个锁,但是如果这个时候进程进行更新重启呢?这个锁要等到超时,才能重新被获得。
结论:用redis控制锁的释放时间,不太靠谱。
3、使用zookeeper 基于zookeeper临时有序节点可以实现的分布式锁。
过程:
客户端连接zk,在某个父节点node上创建子节点,第一个创建临时有序节点的为/node/lock-0000000000,第二个为/node/lock-0000000001,以次类推。
客户端获取node下的子节点列表,判断自己当前是否是最小序号,是则获取到锁。否则监听自己前一个的子节点的删除消息,直到获得锁.
执行业务代码
会话结束删除这个节点,或者超时,zk会自动释放节点
结论:解决了上面死锁,锁不能自动释放,等待过长等问题。
问题:实现起来比较复杂
解决:使用第三方客户端curator。
4、使用转转zzlock(基于etcd做数据一致性协调实现的分布式锁),自动续期,进程挂掉自动回收。
3,4方案使用起来是比较简便,并能很好的解决我们的问题。
三、服务终止后,使用定时任务轮询来恢复中断的任务,任务的竞争采用上面的分布锁实现
增加节点后,beetle简要结构如下:
解决以上问题之后,双活的问题就已经解决了。接下来我们思考一个问题,beetle是编译-部署-上线管理平台,那么beetle能不能上线beetle本身?
是可以的,当我们向“上线服务”发送上线任务后。一台服务器从nginx摘除,请求和回调都落到剩余的机器上。“上线服务”在完成服务跟新重启后,挂会nginx。开始服务。